package com.ruoyi.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.cache.common.CacheConstants;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.service.ServiceImpl;
import com.ruoyi.system.mapper.SysSerialNumberMapper;
import com.ruoyi.system.domain.SysSerialNumber;
import com.ruoyi.system.service.ISysSerialNumberService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.common.State;
import org.springframework.util.Assert;

import javax.validation.constraints.NotBlank;

/**
 * 序列号模板Service业务层处理
 *
 * @author SunLuQuan
 * @date 2022-06-24
 */
@Service
@Slf4j
@CacheConfig(cacheNames = CacheConstants.EhcacheName.CONTINUAL_CACHE_NAME)
public class SysSerialNumberServiceImpl extends ServiceImpl<SysSerialNumberMapper,SysSerialNumber> implements ISysSerialNumberService
{
    private static final Map<String,Queue<String>> SERIAL_NUMBER_MAP ;

    static{
        SERIAL_NUMBER_MAP = new HashMap<>();
    }
    /**
     * 查询序列号模板
     * 
     * @param id 序列号模板主键
     * @param columns 查询指定的列
     * @return 序列号模板
     */
    @Override
    public SysSerialNumber selectById(Long id, SFunction<SysSerialNumber,?> ... columns){

        if(StringUtils.isNotEmpty(columns)){
            return selectOne(lambdaQuery().select(columns).eq(SysSerialNumber::getId,id));
        }
        return super.selectById(id);
    }

    /**
     * 查询序列号模板
         * 注意：只获取状态信息为可用的数据
     * 
     * @param id 序列号模板主键
     * @param columns 查询指定的列
     * @return 序列号模板
     */
    @Override
    public SysSerialNumber selectUsableById(Long id, SFunction<SysSerialNumber,?> ... columns)
    {
            LambdaQueryWrapper<SysSerialNumber> lambdaQueryWrapper = customLambdaQuery().eq(SysSerialNumber::getId,id);
            if(StringUtils.isNotEmpty(columns)){
                lambdaQueryWrapper.select(columns);
            }
            return selectOne(lambdaQueryWrapper);
    }

    /**
     *
     * @param code
     * @return
     */
    private String getMaxSerialNumberInCache(String code){
        if(SERIAL_NUMBER_MAP.containsKey(code)){

            Queue<String> maxSerialNumberQueue = SERIAL_NUMBER_MAP.get(code);
            String maxSerialNumber = maxSerialNumberQueue.remove();
            if(StringUtils.isEmpty(maxSerialNumberQueue)){
                SERIAL_NUMBER_MAP.remove(code);
            }
            return maxSerialNumber;
        }
        return StringUtils.EMPTY;
    }

    /**
     * 自增
     * @param volatileSerialNumber
     * @return
     */
    private String incrementAndGet(String volatileSerialNumber){
        int length = volatileSerialNumber.length();
        Integer num = Convert.toInt(volatileSerialNumber, 0);
        return String.format("%0"+length+"d",++num);
    }

    /**
     * 生成多个连续的自增数量
     * @param volatileSerialNumber
     * @param increment
     * @return
     */
    private Queue<String> continuousIncrementAndGet(String volatileSerialNumber,int increment){
        LinkedList<String> serialNumberListOfCache = new LinkedList();
        String lastSerialNumber = volatileSerialNumber;
        for (int i = 0; i < increment; i++) {
            lastSerialNumber = incrementAndGet(lastSerialNumber);
            serialNumberListOfCache.add(lastSerialNumber);
        }

        return serialNumberListOfCache;
    }

    /**
     * 执行 spel 表达式解析
     * @param spelExpression
     * @param evaluationContext
     * @return
     */
    private String executeSpelExpressionParser(String spelExpression,EvaluationContext evaluationContext){
        ExpressionParser expressionParser = new SpelExpressionParser();

        Expression expression = expressionParser.parseExpression(spelExpression);

        return expression.getValue(evaluationContext,String.class);
    }

    /**
     * 获取最新的序列号
     * @param moduleCode 根据code
     * @return
     */
    @Override
    public String getMaxSerialNumberByCode(String moduleCode) {

        String maxSerialNumber = getMaxSerialNumberInCache(moduleCode);

        SysSerialNumber serialNumberOfParams = new SysSerialNumber();
        serialNumberOfParams.setModuleCode(moduleCode);
        SysSerialNumber serialNumber = selectOne(serialNumberOfParams);

        Assert.notNull(serialNumber,StringUtils.format("序列号模块标识：{}不存在或不可用！",moduleCode));

        SysSerialNumber serialNumberOfUpdate = new SysSerialNumber();

        serialNumberOfUpdate.setId(serialNumber.getId());

        if(StringUtils.isEmpty(maxSerialNumber)){

            Integer count = serialNumber.getCount();
            String maxSerial = serialNumber.getMaxSerial();
            String resetTag = serialNumber.getResetTag();
            Integer preMaxNum = serialNumber.getPreMaxNum();
            String currentTag = DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD);
            if(count>0){
                //如果重置标记为空  || 如果当前重置标记与数据库中的标记不符
                //则需要重置 存储的最大序列号 更新重置标记
                if(StringUtils.isEmpty(resetTag) || StringUtils.isNotEmpty(maxSerial) && !StringUtils.equals(resetTag,currentTag)){
                    resetTag = currentTag ;
                    maxSerial = StringUtils.EMPTY;
                    serialNumberOfUpdate.setResetTag(resetTag);
                    SERIAL_NUMBER_MAP.remove(moduleCode);
                }

                String volatileSerialNumber ;
                String prefixSerialNumber ;

                if(StringUtils.isNotEmpty(maxSerial)){
                    //获取可变的数值
                    volatileSerialNumber = StringUtils.substring(maxSerial,maxSerial.length()-count);
                    prefixSerialNumber = StringUtils.substringBeforeLast(maxSerial,volatileSerialNumber);
                }else{

                    EvaluationContext evaluationContext = new StandardEvaluationContext(serialNumber);

                    evaluationContext.setVariable("dateUtils",DateUtils.class);

                    prefixSerialNumber = executeSpelExpressionParser(serialNumber.getConfigTemplate(),evaluationContext);

                    volatileSerialNumber = String.format("%0"+count+"d",0);
                }

                if(preMaxNum>1){

                    Queue<String> serialNumberQueueOfCache = continuousIncrementAndGet(volatileSerialNumber, preMaxNum).stream().map(s->prefixSerialNumber+s).collect(Collectors.toCollection(LinkedList::new));

                    SERIAL_NUMBER_MAP.put(moduleCode,serialNumberQueueOfCache);

                    maxSerialNumber = serialNumberQueueOfCache.remove();
                }else{
                    maxSerialNumber = prefixSerialNumber + incrementAndGet(volatileSerialNumber);
                }
            }else{

                EvaluationContext evaluationContext = new StandardEvaluationContext(serialNumber);

                evaluationContext.setVariable("dateUtils",DateUtils.class);

                maxSerialNumber = executeSpelExpressionParser(serialNumber.getConfigTemplate(),evaluationContext);

            }
        }
        serialNumberOfUpdate.setMaxSerial(maxSerialNumber);

        updateById(serialNumberOfUpdate);

        return maxSerialNumber;
    }

    /**
     * 查询序列号模板列表
     *
     * @param sysSerialNumber 序列号模板
     * @return 序列号模板
     */
    @Override
    public List<SysSerialNumber> selectList(SysSerialNumber sysSerialNumber)
    {
        return getBaseMapper().selectSysSerialNumberList(sysSerialNumber);
    }

    /**
     * 获取所有的序列号模板数据
     * 
     * @return Map<Long,SysSerialNumber> 集合
     */
    @Cacheable
    @Override
    public Map<Long,SysSerialNumber> selectAllMap(){

        return getBaseMapper().selectList(customLambdaQuery().select(SysSerialNumber::getId,SysSerialNumber::getModuleName))
                .stream().collect(Collectors.toMap(SysSerialNumber::getId,v->v));
    }

    /**
     * 新增序列号模板
     *
     * @param sysSerialNumber 序列号模板
     * @return 结果
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean insert(SysSerialNumber sysSerialNumber)
    {
        return super.insert(sysSerialNumber);
    }

    /**
     * 新增（批量）序列号模板
     *
     * @param sysSerialNumberList 实体对象集合
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean insertBatch(Collection<SysSerialNumber> sysSerialNumberList, int batchSize) {
        return super.insertBatch(sysSerialNumberList, batchSize);
    }

    /**
     * 删除 序列号模板 根据编号
     * @param id
     * @return true 删除成功 false 删除失败
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean deleteById(Serializable id) {
        return super.deleteById(id);
    }

    /**
     * 批量删除 序列号模板 根据 编号集合
     * @param ids
     * @return true 删除成功 false 删除失败
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean deleteByIds(Collection<? extends Serializable> ids) {
        return super.deleteByIds(ids);
    }

    /**
     * 批量修改 序列号模板 根据唯一编号
     * @param sysSerialNumberList
     * @param batchSize  更新批次数量
     * @return true 修改成功 false 修改失败
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean updateBatchById(Collection<SysSerialNumber> sysSerialNumberList, int batchSize) {
        return super.updateBatchById(sysSerialNumberList, batchSize);
    }

    /**
     * 修改 序列号模板
     * @param  sysSerialNumber
     * @param updateWrapper
     * @return
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean update(SysSerialNumber sysSerialNumber, LambdaUpdateWrapper<SysSerialNumber> updateWrapper) {
        return super.update(sysSerialNumber,updateWrapper);
    }

    /**
     * 修改序列号模板
     *
     * @param sysSerialNumber 序列号模板
     * @return 结果
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean updateById(SysSerialNumber sysSerialNumber)
    {
        return super.updateById(sysSerialNumber);
    }

    /**
     * 修改序列号模板状态 根据 id 序列号模板
     * 
     * @param id 序列号模板主键
     * @param state 数据状态
     * @return 结果
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean updateStateById(Long id, State state){

        return updateById(buildSysSerialNumberForUpdateState(id,state));
    }

    /**
     * 修改多个序列号模板状态 根据 id 序列号模板
     * 
     * @param ids 序列号模板主键
     * @param state 数据状态
     * @return 结果
     */
    @CacheEvict(keyGenerator=CacheConstants.KeyGeneratorName.EHCACHE_KEY_GENERATOR_EVICT)
    @Override
    public boolean updateStateByIds(Collection<Long> ids, State state){

        List<SysSerialNumber> updateSysSerialNumberList = new ArrayList(ids.size());

            ids.forEach(id->updateSysSerialNumberList.add(buildSysSerialNumberForUpdateState(id,state)));

        return updateBatchById(updateSysSerialNumberList);
    }

    /**
     * 构建修改SysSerialNumber 的 修改类
     * @param id 序列号模板主键
     * @param state 数据状态
     * @return SysSerialNumber构建的修改模型
     */
    private SysSerialNumber buildSysSerialNumberForUpdateState(Long id, State state){

        SysSerialNumber sysSerialNumber = new SysSerialNumber();
        sysSerialNumber.setState(state);
        sysSerialNumber.setId(id);

        return sysSerialNumber;
    }

}
